package com.gree.auth.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gree.auth.config.configuration.AppAuthenticationToken;
import com.gree.auth.config.constant.SecurityConstants;
import com.gree.auth.config.store.MyRedisTokenStore;
import com.gree.auth.entity.Result;
import com.gree.auth.util.DecryptUtil;
import com.gree.common.utils.CodeUtils;
import com.gree.common.utils.Constants;
import com.gree.common.utils.MessageUtils;
import com.gree.framework.entity.CusUserDetails;
import com.gree.framework.entity.SysUser;
import com.gree.framework.manager.AsyncManager;
import com.gree.framework.manager.factory.AsyncFactory;
import com.gree.framework.service.GscmUserService;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.nutz.dao.entity.Record;
import org.nutz.lang.util.NutMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.DefaultExpiringOAuth2RefreshToken;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;

@RestController
public class LoginController {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    @Lazy
    private TokenStore tokenStore;

    @Autowired
    private MyRedisTokenStore myRedisTokenStore;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private GscmUserService gscmUserService;

    @Value("${server.port}")
    private String port;

    /**
    登录接口：
    1.记录到登录日志
    2.更新用户登录信息表:(最近一次登录的时间和Ip地址)
    */
    @PostMapping("/oauth2/loginGscm")
    public Object loginCommon(@RequestBody JSONObject jsonobject, HttpServletResponse response, String dateNowStr1) throws Exception {
        String username = jsonobject.getString("username");
        String password = jsonobject.getString("password");
        String comp = jsonobject.getString("comp");

        Result<JSONObject> result = doGet("http://localhost:"+port+"/oauth/token?username="+username+"&password="+password+"&comp="+comp+"&grant_type=gscmmode&scope=server&client_id=gscm_client&client_secret=123456");
        JSONObject jsonObject = result.getData();
        if(result.getCode()==200){
            SysUser userinfo = gscmUserService.finduserWithClient(username,comp,"greescmdb2");
            Record compinfo = gscmUserService.getCompDsca(comp);
            JSONObject jsonObject1 = result.getData();

            jsonObject1.put("user", NutMap.NEW().addv("usid",userinfo.getUsid()).addv("username",userinfo.getNama()).addv("comp",comp).addv("compdsca",compinfo.getString("dsca")));
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(userinfo.getUsid(),comp, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
            javax.servlet.http.Cookie cookies1 = new javax.servlet.http.Cookie("access_token", jsonObject.getString("access_token"));
            cookies1.setPath("/");
            cookies1.setMaxAge(jsonObject.getIntValue("expires_in"));
            cookies1.setHttpOnly(true);
            response.addCookie(cookies1);

            javax.servlet.http.Cookie cookies2 = new javax.servlet.http.Cookie("refresh_token", jsonObject.getString("refresh_token"));
            cookies2.setPath("/");
            cookies2.setHttpOnly(true);
            response.addCookie(cookies2);
        }else{
            JSONObject jsonObjectError = result.getData();
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username,comp, Constants.LOGIN_FAIL, jsonObjectError.getString("error_description")));
        }
        return result;
    }

    @PostMapping("/oauth2/loginSuno")
    public Object loginSuno(@RequestBody JSONObject jsonobject) {
        String username = jsonobject.getString("username");
        String password = jsonobject.getString("password");
        String comp = jsonobject.getString("comp");
        Result<JSONObject> result = doGet("http://localhost:"+port+"/oauth/token?username="+username+"&password="+password+"&comp="+comp+"&grant_type=gscmmode&scope=server&client_id=suno_client&client_secret=123456");
        if(result.getCode()==200){
            SysUser userinfo = gscmUserService.findSuno(username,comp);
            Record compinfo = gscmUserService.getCompDsca(comp);
            JSONObject jsonObject = result.getData();
            jsonObject.put("user", NutMap.NEW().addv("usid",userinfo.getUsid()).addv("username",userinfo.getNama()).addv("comp",comp).addv("compdsca",compinfo.getString("dsca")));
        }
        return result;
    }

    /**
     * 刷新token
     * @param refresh_Token
     * @param response
     * @return
     */
    @GetMapping("/oauth2/refreshToken")
    public Object refreshToken(@RequestParam String refresh_Token, HttpServletResponse response) {
        Result<JSONObject> result = doGet("http://localhost:"+port+"/oauth/token?grant_type=refresh_token&refresh_token="+refresh_Token+"&client_id=gscm_client&client_secret=123456");
        JSONObject jsonObject = result.getData();
        if(result.getCode()==200){
            javax.servlet.http.Cookie cookies1 = new javax.servlet.http.Cookie("access_token", jsonObject.getString("access_token"));
            cookies1.setPath("/");
            cookies1.setMaxAge(jsonObject.getIntValue("expires_in"));
            cookies1.setHttpOnly(true);
            response.addCookie(cookies1);

            javax.servlet.http.Cookie cookies2 = new javax.servlet.http.Cookie("refresh_token", jsonObject.getString("refresh_token"));
            cookies2.setPath("/");
            cookies2.setHttpOnly(true);
            response.addCookie(cookies2);
        }
        return jsonObject;
    }

    public static Result doGet(String url) {
        Result result = new Result();
        Cookie resCookie = null;
        BasicCookieStore store= new BasicCookieStore();
        CloseableHttpClient httpClient =  HttpClients.custom().setDefaultCookieStore(store).build();
        CloseableHttpResponse response = null;
        String resultBody = "";
        int resultCode = 0;
        try {
            HttpGet httpGet = new HttpGet(url);
            response = httpClient.execute(httpGet);
            resultCode = response.getStatusLine().getStatusCode();
            resultBody = EntityUtils.toString(response.getEntity(), "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        result.setCode(resultCode);
        result.setData(JSON.parseObject(resultBody));
        result.setMsg("");
        return result;
    }


    /**
     * 登出(销毁token)
     * @param request
     * @return
     */
    @GetMapping(value = "/oauth2/logout")
    public Result logOut(HttpServletRequest request){
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        CusUserDetails cusUserDetails = (CusUserDetails) authentication.getPrincipal();
        String header = request.getHeader("Authorization");
        String access_token = StringUtils.substringAfter(header, "Bearer ");
        Result result = new Result();
        try {
            if(StringUtils.isNotBlank(access_token)){
                OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(access_token);
                if(oAuth2AccessToken != null){
                    tokenStore.removeAccessToken(oAuth2AccessToken);
                    OAuth2RefreshToken oAuth2RefreshToken = oAuth2AccessToken.getRefreshToken();
                    tokenStore.removeRefreshToken(oAuth2RefreshToken);
                    tokenStore.removeAccessTokenUsingRefreshToken(oAuth2RefreshToken);
                }
            }
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(cusUserDetails.getUsername(),cusUserDetails.getComp(), Constants.LOGOUT, MessageUtils.message("user.logout.success")));
            result.setCode(200);
            result.setMsg("success");
            result.setData("");
            return result;
        } catch (Exception e){
            result.setCode(500);
            result.setMsg("fail");
            result.setData("");
            return result;
        }
    }

    /**
     * token 检验用户信息接口
     * @return
     */
    @GetMapping(value = "/users/current")
    @ResponseBody
    public Map<String, Object> getUser() {
        OAuth2Authentication authentication = (OAuth2Authentication)SecurityContextHolder.getContext().getAuthentication();
        OAuth2AccessToken getAccessToken = myRedisTokenStore.getAccessToken(authentication);

        Map<String, Object> map = new HashMap<>();
        CusUserDetails cusUserDetails = (CusUserDetails) authentication.getUserAuthentication().getPrincipal();
        map.put("user", cusUserDetails);
        map.put("authorities", authentication.getAuthorities());
        String clientId = authentication.getOAuth2Request().getClientId();
        map.put("client_id", clientId);
        //access剩余时间
        int seconds = getAccessToken.getExpiresIn();
        if (getAccessToken != null) {
            if(clientId.equals("gscm_client")){
                if(seconds < cusUserDetails.getExpireTime()/4){
                    if(getAccessToken instanceof DefaultOAuth2AccessToken){
                        DefaultOAuth2AccessToken defaultOAuth2AccessToken = (DefaultOAuth2AccessToken) getAccessToken;
                        OAuth2RefreshToken refreshToken = getAccessToken.getRefreshToken();
                        //重置accesstoken时间
                        Calendar expirecal = Calendar.getInstance();
                        expirecal.setTime(new Date());
                        expirecal.add(Calendar.SECOND,cusUserDetails.getExpireTime().intValue());
                        Date newExpirationDate = expirecal.getTime();
                        defaultOAuth2AccessToken.setExpiration(newExpirationDate);
                        //重置refreshtoken时间
                        Calendar refreshcal = Calendar.getInstance();
                        refreshcal.setTime(new Date());
                        refreshcal.add(Calendar.SECOND,cusUserDetails.getRefreshTime().intValue());
                        Date newRefreshDate  =refreshcal.getTime();
                        DefaultExpiringOAuth2RefreshToken defaultExpiringOAuth2RefreshToken = (DefaultExpiringOAuth2RefreshToken)refreshToken;
                        refreshToken = new DefaultExpiringOAuth2RefreshToken(defaultExpiringOAuth2RefreshToken.getValue(),newRefreshDate);
                        defaultOAuth2AccessToken.setRefreshToken(refreshToken);

                        myRedisTokenStore.storeAccessToken(getAccessToken, authentication);
                        logger.info("renew token");
                    }
                }
            }
        }
        return map;
    }

    /**
     * 获取在线用户信息
     * @return
     */
    @GetMapping(value = "/users/getonlineuser")
    @ResponseBody
    public Object getOnlineUser() {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);
        Set<String> username = redisTemplate.keys("auth:*");
        Iterator<String> iter = username.iterator();
        ArrayList<HashMap> arrayList = new ArrayList<>();
        while(iter.hasNext()){
            HashMap map = new HashMap<String,String>();
            String next = iter.next();
            String[] strarr = next.split(":");
            map.put("token",strarr[1]);
            OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(strarr[1]);
            Authentication authentication = oAuth2Authentication.getUserAuthentication();
            if(authentication instanceof AppAuthenticationToken){
                AppAuthenticationToken appAuthenticationToken = (AppAuthenticationToken) authentication;
                if(appAuthenticationToken.getPrincipal() instanceof CusUserDetails){
                    CusUserDetails cusUserDetails = (CusUserDetails)appAuthenticationToken.getPrincipal();
                    SysUser sysUser = cusUserDetails.getUser();
                    sysUser.setPawd("");
                    map.put("sysUser",sysUser);
                }
            }else{
                continue;
            }
            arrayList.add(map);
        }
        return arrayList;
    }

    /**
     * 获取在线用户数量
     * @return
     */
    @GetMapping(value = "/users/getonlinecount")
    @ResponseBody
    public Object getOnlineCount() {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(redisSerializer);
        Set<String> username = redisTemplate.keys("auth:*");
        return username.size();
    }

    /**
     * 强退某个用户
     * @param tokenId
     * @return
     */
    @DeleteMapping(value = "/users/forcelogout/{tokenId}")
    public Result forceLogout(@PathVariable String tokenId){
        Result result = new Result();
        try {
            if(StringUtils.isNotBlank(tokenId)){
                OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(tokenId);
                if(oAuth2AccessToken != null){
                    tokenStore.removeAccessToken(oAuth2AccessToken);
                    OAuth2RefreshToken oAuth2RefreshToken = oAuth2AccessToken.getRefreshToken();
                    tokenStore.removeRefreshToken(oAuth2RefreshToken);
                    tokenStore.removeAccessTokenUsingRefreshToken(oAuth2RefreshToken);
                }
            }
            result.setCode(200);
            result.setMsg("success");
            result.setData("");
            return result;
        } catch (Exception e){
            result.setCode(500);
            result.setMsg("fail");
            result.setData("");
            return result;
        }
    }

    @GetMapping("/oauth2/getcallback")
    public void auth(String callback, HttpServletRequest httpGet,HttpServletResponse response) throws IOException {
        String access_token = "";
        Cookie[] cookies = httpGet.getCookies();
        for(Cookie cookie:cookies){
            if(cookie.getName().equals("token")){
                access_token = cookie.getValue();
            }
        }
        Map<String, String> data = new HashMap<>();
        data.put("token", access_token);
        OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(access_token);
        Authentication authentication = oAuth2Authentication.getUserAuthentication();
        if(authentication instanceof AppAuthenticationToken) {
            AppAuthenticationToken appAuthenticationToken = (AppAuthenticationToken) authentication;
            if (appAuthenticationToken.getPrincipal() instanceof CusUserDetails) {
                CusUserDetails cusUserDetails = (CusUserDetails) appAuthenticationToken.getPrincipal();
                String usid = cusUserDetails.getUser().getUsid();
                String comp = cusUserDetails.getUser().getComp();
                data.put("usid", usid);
                data.put("comp", comp);
            }
        }
        String jsonData = JSON.toJSONString(data);
        response.setContentType("text/javascript;charset=UTF-8");
        String result = callback + "(" + jsonData + ")";
        response.getWriter().write(result);
    }


    @GetMapping("/oauth2/getcallback2")
    public Object auth2(String token, HttpServletRequest httpGet,HttpServletResponse response) throws IOException {
        Map<String, String> data = new HashMap<>();
        OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(token);
        Authentication authentication = oAuth2Authentication.getUserAuthentication();
        if(authentication instanceof AppAuthenticationToken) {
            AppAuthenticationToken appAuthenticationToken = (AppAuthenticationToken) authentication;
            if (appAuthenticationToken.getPrincipal() instanceof CusUserDetails) {
                CusUserDetails cusUserDetails = (CusUserDetails) appAuthenticationToken.getPrincipal();
                String usid = cusUserDetails.getUser().getUsid();
                String comp = cusUserDetails.getUser().getComp();
                data.put("usid", usid);
                data.put("comp", comp);
            }
        }
        String jsonData = JSON.toJSONString(data);
        response.setContentType("text/javascript;charset=UTF-8");
        return jsonData;
    }

    @GetMapping("/oauth2/changeComp")
    public Object changeComp(@RequestParam String comp){
        //TODO 后面密码修改成不可逆加密需要更新这里
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        CusUserDetails cusUserDetails = (CusUserDetails) authentication.getPrincipal();
        SysUser sysUser = gscmUserService.finduserWithClient(cusUserDetails.getUser().getUsid(),comp,"greescmdb2");
        String password = sysUser.getPawd();
        password = CodeUtils.decodeString(password);
        try {
            password = DecryptUtil.encryptbyaes(password, SecurityConstants.PASSWORDAES_KEY,SecurityConstants.PASSWORDAES_KEY);
            password = URLEncoder.encode(password);
        } catch (Exception e) {
            throw new BadCredentialsException( "error");
        }
        Result<JSONObject> result = doGet("http://localhost:"+port+"/oauth/token?username="+sysUser.getUsid()+"&password="+password+"&comp="+comp+"&grant_type=gscmmode&scope=server&client_id=gscm_client&client_secret=123456");
        return result;
    }

}
